# encoding=utf-8

import traceback
import warnings

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

warnings.filterwarnings(action='ignore', category=UserWarning, module='gensim')

from gensim.models.doc2vec import Doc2Vec


class PageRank(object):
    """ 计算pagerank """

    def __init__(self, X):
        self.X = X  # 转移矩阵
        self.pr = None
        self.move_matrix = np.zeros((X.shape), dtype=np.float)  # 转移矩阵初始化
        self.pagerank = np.zeros((X.shape[0], 1), dtype=np.float)  # pr值的矩阵初始化

    def graph_move(self):
        """
        构造转移矩阵
        0≤ w[i][j] ≤1
        np.sum(w[j]) = 1
        :param a:
        :return:
        """
        X = np.where(abs(self.X) >= 0.4, self.X, 0)
        X_t = np.transpose(X)  # b为a的转置矩阵
        bcol = [X_t[j].sum() for j in range(X.shape[1])]
        for i in range(X.shape[0]):
            for j in range(X.shape[1]):
                self.move_matrix[i][j] = X[i][j] / bcol[j]  # 完成转移矩阵初始化

    def first_pr(self):
        """
        pr值初始化
        :param c:
        :return:
        """
        for i in range(self.X.shape[0]):
            self.pagerank[i] = float(1) / self.X.shape[0]

    def compute_pagerank(self, p, iter_num=100):
        """
        迭代计算pagerank值
        :param p:
        :param move_matrix: 转移矩阵
        :param pr: 初始pr值
        :param iter:
        :return:
        """
        for i in range(iter_num):
            self.pagerank = np.dot(self.move_matrix, self.pagerank)
            print('iteration {0}'.format(i))
        # ===========================================================================
        # i=1
        # while ((v == dot(m, v)).all() == False):  # 判断pr矩阵是否收敛,(v == p*dot(m,v) + (1-p)*v).all()判断前后的pr矩阵是否相等，若相等则停止循环
        #     # print v
        #     v = dot(m, v)
        #     #print((v == p*dot(m,v) + (1-p)*v).all())
        #     print(i)
        #     i+=1
        # ===========================================================================

    def train(self, iter_num=100):
        """ 训练模型 """
        # 1. 计算转义矩阵
        self.graph_move()
        # print(self.move_matrix.shape)  # (1585, 1585)

        # 2. pr值初始化
        self.first_pr()
        # print(self.pr.shape)  # (1585, 1)

        # 3. 迭代更新pr值
        p = 0.85  # 引入浏览当前网页的概率为p,假设p=0.8
        self.compute_pagerank(p=p, iter_num=iter_num)
        print(self.pagerank)

    @staticmethod
    def save(fname, X, delimiter=',', fmt='%.18e'):
        """ 将计算得到的矩阵保存到文件中 """
        try:
            np.savetxt(fname=fname, X=X, delimiter=delimiter, fmt=fmt)
            print('保存到 {} 成功'.format(fname))
        except Exception:
            traceback.print_exc()


def compute_cosine_similarity(model):
    """ 计算doc2vec文档词向量的余弦相似度 """

    # 1. 计算文档词矩阵
    size_document = len(model.docvecs.vectors_docs)
    doc_words_matrix = np.zeros((size_document, 10), dtype=np.float)
    for i, vector in enumerate(model.docvecs.vectors_docs):
        doc_words_matrix[i] = vector

    # 2. 计算文档词矩阵的余弦相似度矩阵
    cosine_a_matrix = cosine_similarity(doc_words_matrix)
    # print(cosine_a_matrix.shape)  # (1585, 1585)

    return cosine_a_matrix


if __name__ == '__main__':
    # 1. 加载doc2vec文档词向量
    doc2vec_model = Doc2Vec.load("results_data/all_model_titles")
    # print(len(model.docvecs.vectors_docs))    # 1585

    # 2. 计算文档词向量的余弦相似度矩阵
    cosine_a_matrix = compute_cosine_similarity(model=doc2vec_model)
    # np.savetxt('results_data/cosine_a_matrix.csv', cosine_a_matrix, delimiter=',', fmt='%.2f')

    # 3. 计算文档的pagerank值
    model = PageRank(X=cosine_a_matrix)
    model.train()
    model.save(fname='results_data/move_matrix.csv', X=model.move_matrix, fmt='%.8f')
    model.save(fname='results_data/pagerank_titles.csv', X=model.pagerank, fmt='%.8f')
